#pragma once

#include <jsoncpp/json/json.h>
#include <algorithm>
#include "index.hpp"

extern const std::string raw_path;

class Seacher
{
public:
    Seacher()
        : _index(nullptr)
    {
    }
    ~Seacher() {}
    void InitSeacher()
    {
        // 1.获取index实例
        _index = Index::GetInstance();
        // 2.构建索引(耗时较久)
        _index->BuildIndex(raw_path);
    }

    void Search(const std::string &query, std::string *json_string)
    {
        // 用于存储一个文档的权值和对应的多个搜索关键词word
        struct doc_word_elem
        {
            doc_word_elem(uint64_t id = 0, int w = 0)
                : doc_id(id), weight(w)
            {
            }

            uint64_t doc_id;
            int weight;
            std::vector<std::string> words;
        };
        // 1.对用户的query进行jieba分词,存放到words中
        std::vector<std::string> words;
        ns_util::JiebaCutString::CutString(query, &words);
        // 2.根据分词后的各个词,进行index查找
        // (注意统一转换大小写,先查倒排,获得倒排拉链)
        std::vector<doc_word_elem> WeightList;
        std::unordered_map<uint64_t, doc_word_elem> WeightHash; // 存储doc_id和其对应的权值大小
        for (std::string &word : words)
        {
            boost::to_lower_copy(word);
            auto invertedList = _index->GetInvertedIndex(word);
            if (invertedList == nullptr)
            {
                continue;
            }
            for (auto &invertedElem : *invertedList)
            {
                uint64_t doc_id = invertedElem.doc_id;
                int weight = invertedElem.weight;
                auto &item = WeightHash[doc_id];
                item.doc_id = doc_id;
                item.weight += weight;
                item.words.push_back(word);
            }
        }
        // 3.汇总查找结果,按照相关性进行降序排序
        for (const auto &item : WeightHash)
        {
            WeightList.push_back(std::move(item.second));
        }
        // std::vector<myPair> WeightList(WeightHash.begin(), WeightHash.end());
        std::sort(WeightList.begin(), WeightList.end(), [](const doc_word_elem &x, const doc_word_elem &y)
                  { return x.weight > y.weight; });
        // 4.根据查找的结果构建json串(title, desc, url)
        Json::Value root;
        for (auto &item : WeightList)
        {
            uint64_t id = item.doc_id;
            Index::DocInfo_t *doc = _index->GetFarwordIndex(id);
            if (doc == nullptr)
            {
                continue;
            }
            Json::Value elem;
            std::string desc;
            GetDesc(doc->content, item.words[0], &desc);
            if (desc.empty())
            {
                continue;
            }
            elem["title"] = doc->title;
            elem["desc"] = desc;
            elem["url"] = doc->url;
            // for debug
            elem["id"] = doc->doc_id;
            elem["weight"] = item.weight;

            root.append(elem);
        }
        Json::FastWriter writer;
        *json_string = writer.write(root);
    }

private:
    void GetDesc(const std::string &content, const std::string &word, std::string *desc)
    {
        const int prev_step = 50;
        const int next_step = 100;
        auto it = std::search(content.begin(), content.end(), word.begin(), word.end());
        if (it == content.end())
        {
            *desc = "None1";
            return;
        }
        int pos = std::distance(content.begin(), it);
        int start = std::max(pos - prev_step, 0);
        int end = std::min(static_cast<int>(content.size()) - 1, pos + next_step);
        if (start > end)
        {
            *desc = "None2";
            return;
        }
        *desc = content.substr(start, end - start);
        if (end == static_cast<int>(content.size()) - 1)
        {
            *desc += "...";
        }
    }

private:
    Index *_index;
};